package com.radar.task.dalian001;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.google.gson.Gson;
import com.radar.algorithm.*;
import com.radar.common.*;
import com.radar.exception.RadarCaclError;
import com.radar.exception.RadarDecodeError;
import com.radar.io.CinradRadar;
import com.radar.io.RadarGridData;
import com.radar.license.LicenseVerify;
import com.radar.task.cd.Constants;
import com.radar.utils.RefObj;
import com.radar.visualize.PolarMap;
import com.radar.visualize.RasterMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.bytedeco.javacpp.indexer.Indexer;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.Mat;
import org.locationtech.jts.geom.Coordinate;
import org.meteoinfo.data.GridData;
import org.meteoinfo.data.meteodata.MeteoDataInfo;
import org.meteoinfo.global.Extent;
import org.meteoinfo.global.PointD;
import org.meteoinfo.script.MeteoInfoData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.NetcdfFileWriter;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Slf4j
public class Forecast001Task {

    NetcdfFileWriter.Version version = NetcdfFileWriter.Version.netcdf3;

    public String getDateStr(File file) {
        return file.getName().contains("Z_RADR_I_Z") ? file.getName().split("_")[4] : file.getName().substring(2, 2 + 14);
    }

    public String getDateStr19(String file) {
        return file.contains("Z_RADR_I_Z") ? file.split("_")[4] + "000" : file.substring(2, 16) + "000";
    }


    @Scheduled(fixedDelay = 30 * 1000)
//    @Scheduled(fixedRate = 5000)
//    @Scheduled(fixedRate = 1000)
    public void read382() {
        if (!Constants.jz_001) {
            return;
        }
        long time = System.currentTimeMillis();
        String[] prds = Constants.rootPath.split(";");
        List<File> files = new ArrayList<>();
        for (String prdinfo : prds) {
            if (FileUtil.isDirectory(prdinfo)) {
                files.addAll(FileUtil.loopFiles(prdinfo, new RegexFileFilter("^LD(\\d{14})(\\d{3}).(VT382|VT346)$")).stream()
                        .sorted(Comparator.comparingLong(i -> Long.parseLong(i.getName().substring(2, 2 + 14))))
                        .collect(Collectors.toList()));
            }
            if (FileUtil.isDirectory(prdinfo)) {
                files.addAll(FileUtil.loopFiles(prdinfo, new RegexFileFilter("^Z_RADR_I_Z(\\d{4})_(\\d{14})_O_DOR_(.*?)_CAP_FMT.(bin.bz2|bin)$")).stream()
                        .sorted(Comparator.comparingLong(i -> Long.parseLong(i.getName().split("_")[4])))
                        .collect(Collectors.toList()));
            }
        }
        for (File file : files) {
            String next = file.getName();
            if (FileUtil.exist(file.getAbsolutePath() + ".OK")) {
                continue;
            }
            int step = 0;
            String pre = null;
            // find pre
            long dt = DateUtil.parse(getDateStr(file), DateConstants.DATE_TIME_FORMAT_YYYYMMDDHHMISS).getTime();
            if (Constants.delayed > 0) {
                if (new Date().getTime() - dt > Constants.delayed * 60L) {
                    continue;
                }
            }
            long min = Long.MAX_VALUE;
            for (int i = 0; i < files.size(); i++) {
                if (files.get(i).getName().equals(file.getName())) {
                    continue;
                }
                if (!FileUtil.exist(files.get(i).getAbsolutePath() + ".OK")) {
                    continue;
                }
                long n = DateUtil.parse(getDateStr(files.get(i)), DateConstants.DATE_TIME_FORMAT_YYYYMMDDHHMISS).getTime();
                if (n >= dt) {
                    continue;
                }
                if (dt - n < 1000 * 60 * 12 && dt - n < min) {
                    min = dt - n;
                    pre = files.get(i).getName();
                    step = (int) ((DateUtil.parse(getDateStr(file), DateConstants.DATE_TIME_FORMAT_YYYYMMDDHHMISS).getTime() -
                            DateUtil.parse(getDateStr(files.get(i)), DateConstants.DATE_TIME_FORMAT_YYYYMMDDHHMISS).getTime()) / (1000 * 60.0));
                }
            }
            try {
                log.info("start parse pre radar file : {} , step : {} ,next radar file : {}", pre, step, next);
                parse382(pre, file, step);
            } catch (Exception e) {
                e.printStackTrace();
                log.info("parse radar erro file : {}", next);
            }
        }
        log.info("sum time : {}", System.currentTimeMillis() - time);
    }

    /**
     * @param pre      之前的文件
     * @param nextFile 下一个文件
     * @param offset   步长
     * @throws Exception
     */
    public void parse382(String pre, File nextFile, int offset) throws Exception {
        String output = Constants.ncPath + File.separator;
        String next = nextFile.getName();
        String input = nextFile.getParentFile().getAbsolutePath() + File.separator;
        File OKFile = new File(input + next + ".OK");
        if (OKFile.exists()) {
            return;
        }
        FileUtil.touch(OKFile);
        FileUtil.mkdir(output);
        // 基本校验结束
        String date = getDateStr19(next);
        FileUtil.mkdir(output + date + File.separator);
        // 解析雷达
        CinradRadar nextCinrad = new CinradRadar();
        FileUtil.del(output + date + File.separator + next + ".nc");
        nextCinrad.setNetcdfFile(output + date + File.separator + next + ".nc", version);
        nextCinrad.parse(input + next, CinradType.RADAR_382);
        if (nextCinrad.datType == FileType.STANDARD) {
            nextCinrad.setCode("Z9730");
            nextCinrad.radar.code = "Z9730";
            nextCinrad.radar.getRadarSite().setLon(119.18);
            nextCinrad.radar.getRadarSite().setLat(39.06);
            nextCinrad.radar.setProjectStep(0.02);
        }

        RadarSite.RadarSiteItem radarSite = nextCinrad.radar.getRadarSite();
        Map<String, Object> result = new HashMap<>();
        result.put("code", nextCinrad.radar.code);
        result.put("alt", radarSite.getAlt());
        result.put("radarSite", radarSite);
        result.put("date", DateUtil.format(nextCinrad.radar.dateTime, DateConstants.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS));
        result.put("type", nextCinrad.type.name());
        result.put("dataType", nextCinrad.datType.name());
        result.put("vcp", nextCinrad.radar.vcp);
        result.put("elevation", nextCinrad.radar.getElevation());
        result.put("rresolution", nextCinrad.radar.getResolution(ProductType.REF));
        result.put("vresolution", nextCinrad.radar.getResolution(ProductType.VEL));
        // 写入基本信息
        FileUtil.writeUtf8String(new Gson().toJson(result), output + date + File.separator + date + "_setting.json");

        // 生成CR
        RadarGridData cr = nextCinrad.radar.getCR();
//        RadarGridData et = nextCinrad.radar.getET();

        RasterMap rasterMap = new RasterMap(nextCinrad.radar);
        PolarMap polarMap = new PolarMap(nextCinrad.radar);
        BufferedImage bufferedImage;
        // draw ref
        for (int i = 0; i < nextCinrad.radar.getElevation().length; i++) {
            bufferedImage = rasterMap.simpleRAW(RasterMap.buildRasterInfo(ProductType.VEL)
                    // 背景颜色
                    .backColor(Color.black)
                    // 是否添加地理信息
                    .drawCity(true)
                    // 是否显示详细信息以及图例
                    .simpleDetail(true)
                    // 是否画范围 半径 km
                    .rings(new int[]{50, 100, 150, 200}), i);
            ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_VEL_" + i + "_nogis.png"));



            bufferedImage = rasterMap.simpleRAW(RasterMap.buildRasterInfo(ProductType.SW)
                    // 背景颜色
                    .backColor(Color.black)
                    // 是否添加地理信息
                    .drawCity(true)
                    // 是否显示详细信息以及图例
                    .simpleDetail(true)
                    // 是否画范围 半径 km
                    .rings(new int[]{50, 100, 150, 200}), i);
            ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_SW_" + i + "_nogis.png"));



            bufferedImage = rasterMap.simpleRAW(RasterMap.buildRasterInfo(ProductType.REF)
                    // 背景颜色
                    .backColor(Color.black)
                    // 是否添加地理信息
                    .drawCity(true)
                    // 是否显示详细信息以及图例
                    .simpleDetail(true)
                    // 是否画范围 半径 km
                    .rings(new int[]{50, 100, 150, 200}), i);
            ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_REF_" + i + "_nogis.png"));
        }

        for (int i = 0; i < nextCinrad.radar.getElevation().length; i++) {
            bufferedImage = rasterMap.simplePPI(RasterMap.buildRasterInfo(ProductType.VEL).backColor(new Color(0, 0, 0, 0)), i);
            ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_VEL_" + i + ".png"));

            bufferedImage = rasterMap.simplePPI(RasterMap.buildRasterInfo(ProductType.SW).backColor(new Color(0, 0, 0, 0)), i);
            ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_SW_" + i + ".png"));

            bufferedImage = rasterMap.simplePPI(RasterMap.buildRasterInfo(ProductType.REF).backColor(new Color(0, 0, 0, 0)), i);
            ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_REF_" + i + ".png"));
        }

        log.info("draw ref radar end file : {}", next);

        // draw cr
        // 生成CR图片
        RasterMap.RasterInfo rasterInfo = RasterMap.buildRasterInfo(ProductType.REF)
                // 背景颜色
                .backColor(Color.black)
                // 是否添加地理信息
                .drawCity(true)
                // 是否显示详细信息以及图例
                .simpleDetail(true)
                // 是否画范围 半径 km
                .rings(new int[]{50, 100, 150, 200, 300, 400});

        byte[][] bytes = nextCinrad.radar.quickCR();
        RadarGridData radarGridData = new RadarGridData();
        radarGridData.radarData = bytes;

        bufferedImage = rasterMap.simpleRaster(rasterInfo,radarGridData);
//        bufferedImage = rasterMap.simpleCR(rasterInfo);;
        ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_CR_0_nogis.png"));

        bufferedImage = rasterMap.drawSingleRaster(MathCommon.decode(cr.radarData, ProductType.REF), ProductType.REF, new Color(0, 0, 0, 0));
        ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_CR_0.png"));

//        log.info("draw cr radar end file : {}", next);
//        // draw gray
//        double[][] gray = MathCommon.decode(cr.radarData, ProductType.REF);
//        gray = Cacl.nineMedianFilter(gray);
//        gray = Cacl.nineMedianFilter(gray);
//        gray = Cacl.nineMedianFilter(gray);

//        bufferedImage = rasterMap.drawSingleRaster(gray, ProductType.REFGRAY);
//        ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + "CR_GRAY.png"));
        log.info("draw cr_gray end radar file : {}", next);
        // find pre GRAY
        double[][][] dir = (double[][][]) null;
        CinradRadar preCinrad = null;
        if (pre != null) {
            String predt = getDateStr19(pre);
            preCinrad = new CinradRadar();
            preCinrad.setNetcdfFile(output + predt + File.separator + pre + ".nc", version);
            preCinrad.parse(input + pre, CinradType.RADAR_382);
            if (preCinrad.datType == FileType.STANDARD) {
                preCinrad.setCode("Z9730");
                preCinrad.radar.code = "Z9730";
                preCinrad.radar.getRadarSite().setLon(119.18);
                preCinrad.radar.getRadarSite().setLat(39.06);
                preCinrad.radar.setProjectStep(0.02);
//                Optional<RadarSite.RadarSiteItem> z9417 = RadarSite.getInstance().getItem("Z9417");
//                nextCinrad.setCode("Z9417");
//                nextCinrad.radar.code = "Z9417";
//                nextCinrad.radar.getRadarSite().setLon(z9417.get().getLon());
//                nextCinrad.radar.getRadarSite().setLat(z9417.get().getLat());
//                nextCinrad.radar.setProjectStep(0.02);
            }
        }

        if (pre != null) {
            try {
                if (Math.abs(preCinrad.radar.getRadarSite().getLon() - nextCinrad.radar.getRadarSite().getLon()) < 1
                        &&
                        Math.abs(preCinrad.radar.getRadarSite().getLat() - nextCinrad.radar.getRadarSite().getLat()) < 1
                ) {
                    // 开始拉格朗日外推
//                    log.info("start forecast pre radar file : {} next radar file : {} , time :{}", pre, next, System.currentTimeMillis());
//                    dir = forecast(output + getDateStr19(pre) + File.separator + "CR_GRAY.png",
//                            output + date + File.separator + "CR_GRAY.png",
//                            offset, cr, next, nextCinrad.datType == FileType.STANDARD);
//                    log.info("end forecast pre radar file : {} next radar file : {} , time :{}", pre, next, System.currentTimeMillis());
                } else {
                    log.info("error lon lat abs > 1 forecast pre radar file : {} next radar file : {}", pre, next);
                }
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }
        log.info("start calc storm data time : {}, file : {}", System.currentTimeMillis(), next);
        // output storms 计算
        RefObj<Integer> count1 = new RefObj<>(0);
        int step = 8;
//        double[][] decode = MathCommon.decode(cr.radarData, ProductType.REF);
//        double[][] data = new double[decode.length / step][decode[0].length / step];
//         降低分辨率 不然算的太慢
//        for (int i = 0; i < data.length; i++) {
//            for (int j = 0; j < data[0].length; j++) {
//                data[i][j] = decode[i * step][j * step];
//            }
//        }
//        List<Storms.StormDt> list = Storms.getStorms(data, step, count1, 32, 1000, nextCinrad);
//        if (list.size() > 0 && list.stream().filter(i -> i.getArea() > 8000).count() > 0) {
        // 设置为 错误数据 排除掉
        nextCinrad.radar.flush();
        log.info("write storm and extent radar file : {}", next);
        FileUtil.writeString(cr.getXMin() + ";" + cr.getXMax() + ";" + cr.getYMin() + ";" + cr.getYMax(), new File(output + date + File.separator + date + "_extent.json"), Charset.defaultCharset());
        FileUtil.touch(OKFile);
//        } else {
//            step = 4;
//            data = new double[decode.length / step][decode[0].length / step];
//            for (int i = 0; i < data.length; i++) {
//                for (int j = 0; j < data[0].length; j++) {
//                    data[i][j] = decode[i * step][j * step];
//                }
//            }
//            list = Storms.getStorms(data, step, count1, 32, 1000, nextCinrad);
//        }
//        Gson gson = new Gson();
//         判断上一个json 是否存在
//        if (pre != null) {
//            double[][] decodePre = MathCommon.decode(preCinrad.radar.getCR().radarData, ProductType.REF);
//            double[][] dataPre = new double[decodePre.length / step][decodePre[0].length / step];
//            for (int i = 0; i < dataPre.length; i++) {
//                for (int j = 0; j < dataPre[0].length; j++) {
//                    dataPre[i][j] = decodePre[i * step][j * step];
//                }
//            }
//            RefObj<Integer> count = new RefObj<>(0);
//            List<Storms.StormDt> list0 = Storms.getStorms(dataPre, step, count, 32, 1000, preCinrad);
        // 计算风暴外推
//            List<Storms.StormDt> track = Storms.track(list0, list, nextCinrad, 8000);
//            if (track != null) {
//                list = track;
//            }
//        }
//        double[][][] finalDir = dir;

        // 用光流替代titan
//        if (pre != null && finalDir != null) {
//            // 写入 uv 图片
//            int h = Math.min(finalDir[0].length, decode.length);
//            int w = Math.min(finalDir[0][0].length, decode[0].length);
//            // 生成风场
//            bufferedImage = rasterMap.drawSingleRaster(MathCommon.decode(cr.radarData, ProductType.REF), ProductType.REF, new Color(0, 0, 0, 0));
//            BufferedImage br = new BufferedImage(w, h, 2);
//            Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
//            Graphics2D graphics1 = (Graphics2D) br.getGraphics();
//            graphics.setColor(Color.black);
//            graphics1.setColor(Color.black);
//            for (int ii = 0; ii < h; ii += 25) {
//                for (int jj = 0; jj < w; jj += 25) {
//                    double x = finalDir[0][ii][jj];
//                    double y = finalDir[1][ii][jj];
////                    if (decode[ii][jj] > 5) {
//                    double ws = Math.sqrt(x * x + y * y);
//                    WindFlagSymbol wfs = new WindFlagSymbol(ws);
//                    wfs.rotateTo(x, y);
//                    List<Line2D> awtLines = wfs.getAwtLines(new Coordinate(jj, ii), 15);
//                    awtLines.forEach(graphics::draw);
//                    awtLines.forEach(graphics1::draw);
//
//                    graphics.setFont(new Font("Arial", 0, 10));
//                    graphics1.setFont(new Font("Arial", 0, 10));
//                    graphics.drawString((int) WindFlagSymbol.getDirAndSpeed(x, y) + "", jj, ii);
//                    graphics1.drawString((int) WindFlagSymbol.getDirAndSpeed(x, y) + "", jj, ii);
////                        graphics.drawString((int)ws +"",j,i);
////                        WindFlagSymbol windFlagSymbol = new WindFlagSymbol();
////                        windFlagSymbol.rotateTo(WindFlagSymbol.getDirAndSpeed180(x, y));
////                        List<WindFlagSymbol.WindFlagLine> actualLines = windFlagSymbol.getActualLines(new Coordinate(j, i), 10);
////                        actualLines.forEach(k -> graphics.drawLine((int) k.getA().x, (int) k.getA().y, (int) k.getB().x, (int) k.getB().y));
////                        actualLines.forEach(k -> graphics1.drawLine((int) k.getA().x, (int) k.getA().y, (int) k.getB().x, (int) k.getB().y));
////                    }
//                }
//            }
//            ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_CR_UV.png"));
//            ImageIO.write(br, "png", new File(output + date + File.separator + date + "_CR_UV_BLANK.png"));
//        }

//        list.forEach(z -> {
//            if (pre != null && finalDir != null) {
//                double u = 0.0D;
//                double v = 0.0D;
//                int xindex = (int) (z.getCenter()).Y;
//                int yindex = (int) (z.getCenter()).X;
//                int index = 0;
//                for (int i = yindex - 4; i <= yindex + 4; i++) {
//                    for (int j = xindex - 4; j <= xindex + 4; j++) {
//                        if (decode[i][j] > 5 && j > 0 && i > 0 && i < finalDir[0].length - 1 && j < finalDir[0][0].length - 1) {
//                            u += finalDir[0][i][j];
//                            v += finalDir[1][i][j];
//                            index++;
//                        }
//                    }
//                }
//                if (index > 0) {
//                    u /= index;
//                    v /= index;
//                    z.setWd(WindFlagSymbol.getDirAndSpeed(u, v));
//                    z.setWs(nextCinrad.radar.getResolution(ProductType.CR) / offset / 60 * uv2wind(u, v)[0]);
//                }
//            }
//            z.setCenter(
//                    new PointD(cr.xArray[(int) z.getCenter().Y],
//                            cr.yArray[cr.yArray.length - 1 - (int) z.getCenter().X]));
//            if (z.getWs() != -999 && z.getWd() != -999) {
//                z.setForecastCenter(computerThatLonLat(z.getCenter(), z.getWd(), z.getWs() * 120 * 60));
//            }
//            List<double[]> list1 = new ArrayList<>();
//            z.getBoundary().forEach((point) -> {
//                double x = cr.xArray[(int) point.Y];
//                double y = cr.yArray[cr.yArray.length - 1 - (int) point.X];
//                point.X = x;
//                point.Y = y;
//                list1.add(new double[]{y, x});
//            });
//            if (et != null) {
//                z.setHeight(et.getValue(z.getCenter().X, z.getCenter().Y));
//            }
//            z.setBoundary707(list1);
//            z.getContain().forEach((point) -> {
//                double x = cr.xArray[(int) point.Y];
//                double y = cr.yArray[cr.yArray.length - 1 - (int) point.X];
//                point.X = x;
//                point.Y = y;
//            });
//        });
//        nextCinrad.radar.flush();
//        log.info("end write storm and extent radar time: {} ,file : {}", System.currentTimeMillis(), next);
//        FileUtil.writeString(gson.toJson(list), new File(output + date + File.separator + date + "_strom.json"), Charset.defaultCharset());
//        FileUtil.writeString(cr.getXMin() + ";" + cr.getXMax() + ";" + cr.getYMin() + ";" + cr.getYMax(), new File(output + date + File.separator + date + "_extent.json"), Charset.defaultCharset());
//        FileUtil.touch(OKFile);
    }

    public double[][][] forecast(String preImg, String nextImg, int offset, RadarGridData cr, String filename, boolean 测试样例) throws IOException {
        String output = Constants.ncPath + File.separator;
        String date = getDateStr19(filename);
        Mat ff1 = opencv_imgcodecs.imread(preImg);
        Mat ff2 = opencv_imgcodecs.imread(nextImg);
        Mat prvs = ff1.clone();
        Mat next = ff1.clone();
        opencv_imgproc.cvtColor(ff1, prvs, opencv_imgproc.COLOR_BGR2GRAY);
        opencv_imgproc.cvtColor(ff2, next, opencv_imgproc.COLOR_BGR2GRAY);

        int step = 8;

        int w = Math.min(prvs.size().get(0), next.size().get(0)) / step;
        int h = Math.min(prvs.size().get(1), next.size().get(1)) / step;

        double[][] pre = new double[h][w];
        double[][] nex = new double[h][w];
        Indexer indexer = prvs.createIndexer();
        Indexer indexer1 = next.createIndexer();

        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                pre[i][j] = indexer.getDouble(i * step, j * step);
                if (pre[i][j] == 255) {
                    pre[i][j] = 0;
                }
                nex[i][j] = indexer1.getDouble(i * step, j * step);
                if (nex[i][j] == 255) {
                    nex[i][j] = 0;
                }
            }
        }


        double[][][] cacl = DenseLucaskanade.cacl(new double[][][]{pre, nex});
        double[][] uu = cacl[0];
        double[][] vv = cacl[1];
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                uu[i][j] = uu[i][j] * (测试样例 ? 1.5 : 6.0 / offset);
                vv[i][j] = vv[i][j] * (测试样例 ? 1.5 : 6.0 / offset);
            }
        }

        uu = Cacl.nineMedianFilter(uu);
        uu = Cacl.nineMedianFilter(uu);
        vv = Cacl.nineMedianFilter(vv);
        vv = Cacl.nineMedianFilter(vv);

        double[][] u = new double[uu.length * step][uu[0].length * step];
        double[][] v = new double[uu.length * step][uu[0].length * step];
        for (int i = 0; i < u.length; i++) {
            for (int j = 0; j < u[0].length; j++) {
                u[i][j] = uu[i / step][j / step];
                v[i][j] = vv[i / step][j / step];
            }
        }

        double[][] decode = MathCommon.decode(cr.radarData, ProductType.REF);
        if (decode.length == u.length && decode[0].length == u[0].length) {

        } else {
            double[][] z = new double[u.length][u[0].length];
            for (int i = 0; i < u.length; i++) {
                for (int j = 0; j < u[0].length; j++) {
                    if (i >= decode.length || j >= decode[0].length) {
                        z[i][j] = GisConstants.UNDEF;
                    } else {
                        z[i][j] = decode[i][j];
                    }
                }
            }
            decode = z;
        }
        for (int i = 0; i < u.length; i++) {
            for (int j = 0; j < u[0].length; j++) {
                u[i][j] = uu[i / step][j / step];
                v[i][j] = vv[i / step][j / step];
            }
        }
        double[][][] dir = new double[2][u.length][(u[0]).length];
        for (int k = 0; k < u.length; k++) {
            for (int n = 0; n < (u[0]).length; n++) {
//                if (decode[k][n] < 2) {
//                    u[k][n] = Float.NaN;
//                    v[k][n] = Float.NaN;
//                }
                dir[0][k][n] = u[k][n];
                dir[1][k][n] = v[k][n];
            }
        }
        SemiLagrangian semiLagrangian = new SemiLagrangian();
        int t = 20;
        float[][][] extrapolate = semiLagrangian.extrapolate(MathCommon.doubleToFloat(decode), MathCommon.doubleToFloat(u), MathCommon.doubleToFloat(v), t);
//        cinradRadar.radar.writeSemiLagrangian(extrapolate, 5, t);
        for (int i = 0; i < t; i++) {
            BufferedImage bufferedImage;
            for (int j = 0; j < u.length; j++) {
                for (int k = 0; k < u[0].length; k++) {
                    extrapolate[i][j][k] = extrapolate[i][j][k] == 0 || Float.isNaN(extrapolate[i][j][k]) ? GisConstants.UNDEF : extrapolate[i][j][k];
                }
            }
            RasterMap rasterMap = new RasterMap(null);
            bufferedImage = rasterMap.drawSingleRaster(MathCommon.floatToDouble(extrapolate[i]), new Color(0, 0, 0, 0));
            ImageIO.write(bufferedImage, "png", new File(output + date + File.separator + date + "_CR_" + (i + 1) + ".png"));
        }
        return dir;
    }

    public static double[] uv2wind(double u, double v) {
        double[] ws = new double[2];
        ws[0] = Math.sqrt(u * u + v * v);
        double dir = 270.0D - Math.atan2(v, u) * 180.0D / Math.PI;
        if (dir >= 360.0D)
            dir -= 360.0D;
        if (dir < 0.0D)
            dir += 360.0D;
        ws[1] = dir;
        return ws;
    }


    private static double a = 6378137;
    private static double b = 6356752.3142;
    private static double f = 1 / 298.2572236;

    public static PointD computerThatLonLat(PointD pointD, double brng, double dist) {
        double lon = pointD.X;
        double lat = pointD.Y;
        double alpha1 = rad(brng);
        double sinAlpha1 = Math.sin(alpha1);
        double cosAlpha1 = Math.cos(alpha1);
        double tanU1 = (1 - f) * Math.tan(rad(lat));
        double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1));
        double sinU1 = tanU1 * cosU1;
        double sigma1 = Math.atan2(tanU1, cosAlpha1);
        double sinAlpha = cosU1 * sinAlpha1;
        double cosSqAlpha = 1 - sinAlpha * sinAlpha;
        double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
        double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
        double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));

        double cos2SigmaM = 0;
        double sinSigma = 0;
        double cosSigma = 0;
        double sigma = dist / (b * A), sigmaP = 2 * Math.PI;
        while (Math.abs(sigma - sigmaP) > 1e-12) {
            cos2SigmaM = Math.cos(2 * sigma1 + sigma);
            sinSigma = Math.sin(sigma);
            cosSigma = Math.cos(sigma);
            double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)
                    - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
            sigmaP = sigma;
            sigma = dist / (b * A) + deltaSigma;
        }

        double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
        double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
                (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
        double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
        double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
        double L = lambda - (1 - C) * f * sinAlpha
                * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));

        double revAz = Math.atan2(sinAlpha, -tmp); // final bearing
        return new PointD(lon + deg(L), deg(lat2));
    }

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    private static double deg(double x) {
        return x * 180 / Math.PI;
    }


    public static void main(String[] args) throws Exception {
        // 测试 别的风场

        MeteoDataInfo meteoDataInfo = new MeteoInfoData().openGrADSData("Z:\\cctv-out\\Grads\\HJG2DQ\\2022061612\\HJG2DQ_2022061612_UU.ctl");
        GridData udata = meteoDataInfo.getGridData();
        MeteoDataInfo meteoDataInfo1 = new MeteoInfoData().openGrADSData("Z:\\cctv-out\\Grads\\HJG2DQ\\2022061612\\HJG2DQ_2022061612_VV.ctl");
        GridData vdata = meteoDataInfo1.getGridData();
        BufferedImage br = new BufferedImage(udata.getXNum() * 10, udata.getYNum() * 10, 2);
        Graphics2D graphics = (Graphics2D) br.getGraphics();
        graphics.setColor(Color.black);
        for (int ii = 0; ii < udata.getYNum(); ii += 5) {
            for (int jj = 0; jj < udata.getXNum(); jj += 5) {
                double x = udata.data[udata.getYNum() - 1 - ii][jj];
                double y = vdata.data[udata.getYNum() - 1 - ii][jj];
                double ws = Math.sqrt(x * x + y * y);
                WindFlagSymbol wfs = new WindFlagSymbol(ws);
                wfs.rotateTo(x, y);
                List<Line2D> awtLines = wfs.getAwtLines(new Coordinate(jj * 10, ii * 10), 15);
                awtLines.forEach(graphics::draw);
//                lines.forEach(k -> graphics1.drawLine((int) k.getA().x, (int) k.getA().y, (int) k.getB().x, (int) k.getB().y));
//                        Font f = new Font("宋体", Font.PLAIN, 8);
//                        graphics.setFont(f);
                graphics.setFont(new Font("Arial", 0, 10));
                graphics.drawString((int) WindFlagSymbol.getDirAndSpeed180(x, y) + "", jj * 10, ii * 10);
            }
        }
        ImageIO.write(br, "png", new File("D:\\test.png"));

//        FileUtil.loopFiles("E:\\data\\radar\\radardata\\Z9692", new RegexFileFilter("^Z_RADR_I_Z(\\d{4})_(\\d{14})_O_DOR_(.*?)_CAP_FMT.(bin.bz2|bin)")).forEach(i -> {
//            CinradRadar cinradRadar = new CinradRadar();
//            try {
//                cinradRadar.parse(i.getAbsolutePath());
//                String file = "E:\\data\\RADA\\个例\\Z9692\\" + "LD" + (DateUtil.format(cinradRadar.radar.dateTime, DateConstants.DATE_TIME_FORMAT_YYYYMMDDHHMISS)) + "454.VT346";
//                cinradRadar.radar.close();
//                FileUtil.copy(i, new File(file), true);
//            } catch (RadarDecodeError radarDecodeError) {
//                radarDecodeError.printStackTrace();
//            }
//        });


        // X = 121.717283356843
        // Y = 38.72172103719474
//        PointD otherPoint = computerThatLonLat(new PointD(121.71, 38.721), 58.596599658612774, 11 * 120 * 60);
//        PointD otherPoint1 = computerThatLonLat(new PointD(121.71, 38.721), 58.596599658612774, 11 * 120 * 60 / 1000.0);
//        System.out.println();
//        CinradRadar cinradRadar = new CinradRadar();
//        cinradRadar.parse("Z:\\LD20220424200947744.VT382\\LD20130906150301453.VT346", CinradType.RADAR_382);
//        PolarMap polarMap = new PolarMap(cinradRadar.radar);
//        for (int i = 0; i < cinradRadar.radar.getElevation().length; i++) {
//            BufferedImage bufferedImage = new RasterMap(cinradRadar.radar).simplePPI(RasterMap.buildRasterInfo(ProductType.REF), i);
////            BufferedImage bufferedImage = polarMap.simplePolar(PolarMap.
////                    buildPolarInfo(i, ProductType.REF)
////                    .simpleDetail(true));
//            ImageIO.write(bufferedImage, "png", new File("D:\\test" + i + ".png"));
//        }
//        BufferedImage crimg = new RasterMap(cinradRadar.radar).simpleCR(RasterMap.buildRasterInfo(ProductType.CR));
//        ImageIO.write(crimg, "png", new File("D:\\cr.png"));
//        cinradRadar.radar.flush();
    }

}
